using System;
using System.IO;
using System.Text;

namespace MinecraftGPS.Data
{
    class NBTBinaryReader : IDisposable
    {
        private Stream m_stream;
        private byte[] m_buffer;
        private char[] m_charBuffer;
        private byte[] m_charBytes;
        private Decoder m_decoder;
        private int m_maxCharsSize;

        public NBTBinaryReader(Stream input)
        {
            this.m_stream = input;
            var encoding = new UTF8Encoding();
            this.m_decoder = encoding.GetDecoder();
            this.m_maxCharsSize = encoding.GetMaxCharCount(0x80);
            int maxByteCount = encoding.GetMaxByteCount(1);
            if (maxByteCount < 0x10)
            {
                maxByteCount = 0x10;
            }
            this.m_buffer = new byte[maxByteCount];
            this.m_charBuffer = null;
            this.m_charBytes = null;
        }

        public virtual void Close()
        {
            this.Dispose(true);
        }

        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                Stream stream = this.m_stream;
                this.m_stream = null;
                if (stream != null)
                {
                    stream.Close();
                }
            }
            this.m_stream = null;
            this.m_buffer = null;
            this.m_decoder = null;
            this.m_charBytes = null;
            this.m_charBuffer = null;
        }

        private void FillBuffer(int numBytes)
        {
            int offset = 0;
            int num2 = 0;
            if (numBytes == 1)
            {
                num2 = this.m_stream.ReadByte();
                this.m_buffer[0] = (byte)num2;
            }
            else
            {
                do
                {
                    num2 = this.m_stream.Read(this.m_buffer, offset, numBytes - offset);
                    offset += num2;
                }
                while (offset < numBytes);
            }
        }

        private int InternalReadChars(char[] buffer, int index, int count)
        {
            int num = 0;
            int num2 = 0;
            int num3 = count;
            if (this.m_charBytes == null)
            {
                this.m_charBytes = new byte[0x80];
            }
            while (num3 > 0)
            {
                num2 = num3;
                if (num2 > 0x80)
                {
                    num2 = 0x80;
                }
                num2 = this.m_stream.Read(this.m_charBytes, 0, num2);
                if (num2 == 0)
                {
                    return (count - num3);
                }
                num = this.m_decoder.GetChars(this.m_charBytes, 0, num2, buffer, index);
                num3 -= num;
                index += num;
            }
            return count;
        }

        public virtual int ReadByte()
        {
            return this.m_stream.ReadByte();
        }

        public byte[] ReadBytes(int count)
        {
            byte[] buffer = new byte[count];
            int offset = 0;
            do
            {
                int num2 = this.m_stream.Read(buffer, offset, count);
                if (num2 == 0)
                {
                    break;
                }
                offset += num2;
                count -= num2;
            }
            while (count > 0);
            if (offset != buffer.Length)
            {
                byte[] dst = new byte[offset];
                Buffer.BlockCopy(buffer, 0, dst, 0, offset);
                buffer = dst;
            }
            return buffer;
        }

        public char[] ReadChars(int count)
        {
            char[] buffer = new char[count];
            int num = this.InternalReadChars(buffer, 0, count);
            if (num != count)
            {
                char[] dst = new char[num];
                Buffer.BlockCopy(buffer, 0, dst, 0, 2 * num);
                buffer = dst;
            }
            return buffer;
        }

        public unsafe double ReadDouble()
        {
            this.FillBuffer(8);
            ulong num1 = (((ulong)this.m_buffer[7] | ((ulong)this.m_buffer[6] << 8)) | ((ulong)this.m_buffer[5] << 0x10)) | ((ulong)this.m_buffer[4] << 0x18);
            ulong num2 = (((ulong)this.m_buffer[3] | ((ulong)this.m_buffer[2] << 8)) | ((ulong)this.m_buffer[1] << 0x10)) | ((ulong)this.m_buffer[0] << 0x18);
            ulong num3 = (num2 << 0x20) | num1;
            return *(((double*)&num3));
        }

        public short ReadInt16()
        {
            this.FillBuffer(2);
            return (short)(this.m_buffer[1] | (this.m_buffer[0] << 8));
        }

        public int ReadInt32()
        {
            this.FillBuffer(4);
            return (((this.m_buffer[3] | (this.m_buffer[2] << 8)) | (this.m_buffer[1] << 0x10)) | (this.m_buffer[0] << 0x18));
        }

        public long ReadInt64()
        {
            this.FillBuffer(8);
            ulong num1 = (((ulong)this.m_buffer[7] | ((ulong)this.m_buffer[6] << 8)) | ((ulong)this.m_buffer[5] << 0x10)) | ((ulong)this.m_buffer[4] << 0x18);
            ulong num2 = (((ulong)this.m_buffer[3] | ((ulong)this.m_buffer[2] << 8)) | ((ulong)this.m_buffer[1] << 0x10)) | ((ulong)this.m_buffer[0] << 0x18);
            return (long)((num1) | (num2 << 0x20));
        }

        public unsafe float ReadSingle()
        {
            this.FillBuffer(4);
            uint num = (uint)(((this.m_buffer[3] | (this.m_buffer[2] << 8)) | (this.m_buffer[1] << 0x10)) | (this.m_buffer[0] << 0x18));
            return *(((float*)&num));
        }

        public string ReadString()
        {
            int num = 0;
            int capacity = this.ReadInt16();
            if (capacity == 0)
            {
                return string.Empty;
            }
            if (this.m_charBytes == null)
            {
                this.m_charBytes = new byte[0x80];
            }
            if (this.m_charBuffer == null)
            {
                this.m_charBuffer = new char[this.m_maxCharsSize];
            }
            StringBuilder builder = null;
            do
            {
                int count = ((capacity - num) > 0x80) ? 0x80 : (capacity - num);
                int byteCount = this.m_stream.Read(this.m_charBytes, 0, count);
                int length = this.m_decoder.GetChars(this.m_charBytes, 0, byteCount, this.m_charBuffer, 0);
                if ((num == 0) && (byteCount == capacity))
                {
                    return new string(this.m_charBuffer, 0, length);
                }
                if (builder == null)
                {
                    builder = new StringBuilder(capacity);
                }
                builder.Append(this.m_charBuffer, 0, length);
                num += byteCount;
            }
            while (num < capacity);
            return builder.ToString();
        }

        #region IDisposable Members

        void IDisposable.Dispose()
        {
            this.Dispose(true);
        }

        #endregion
    }
}
